/***************************************************************
|	File:		Matrix.cpp
|	Author:		Michael Mozdzierz
|	Date:		05/26/2014
|
***************************************************************/

#include "stdafx.h"
#include "Matrix.h"
#include "Vector3.h"
#include "Vector4.h"
#include <math.h>
#include <assert.h>

/*static*/ Matrix Matrix::CreateIdentity(void)
{
	return
	{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f,
	};
}

Matrix::Matrix(void)
{
	for (int i = 0; i < 16; i++)
		e[i] = 0.f;
}
Matrix::Matrix(float m11, float m12, float m13, float m14,
	float m21, float m22, float m23, float m24,
	float m31, float m32, float m33, float m34,
	float m41, float m42, float m43, float m44)
{
	this->m11 = m11;
	this->m12 = m12;
	this->m13 = m13;
	this->m14 = m14;	
	
	this->m21 = m21;
	this->m22 = m22;
	this->m23 = m23;
	this->m24 = m24;

	this->m31 = m31;
	this->m32 = m32;
	this->m33 = m33;
	this->m34 = m34;

	this->m41 = m41;
	this->m42 = m42;
	this->m43 = m43;
	this->m44 = m44;
}

#pragma region Matrix helper functions
/*static*/ Matrix Matrix::CreateRotationX(float rotation)
{
	float _sin = sin(rotation);
	float _cos = cos(rotation);

	return
	{
		1.f, 0.f,	0.f,	0.f,
		0.f, _cos,	-_sin,	0.f,
		0.f, _sin,	_cos,	0.f,
		0.f, 0.f,	0.f,	1.f,
	};
	
}
/*static*/ Matrix Matrix::CreateRotationY(float rotation)
{
	float _sin = sin(rotation);
	float _cos = cos(rotation);

	return
	{
		_cos,  0.f, _sin, 0.f,
		0.f,   1.f, 0.f,  0.f,
		-_sin, 0.f, _cos, 0.f,
		0.f,   0.f, 0.f,  1.f
	};
}
/*static*/ Matrix Matrix::CreateRotationZ(float rotation)
{
	float _sin = sin(rotation);
	float _cos = cos(rotation);

	return
	{
		_cos, -_sin, 0.f, 0.f,
		_sin, _cos,  0.f,  0.f,
		0.f,  0.f,   0.f,  0.f,
		0.f,  0.f,   0.f,  1.f,
	};
}
				  
/*static*/ Matrix Matrix::Translation(Vector3 offset)
{
	return
	{
		1.f, 0.f, 0.f, offset.x,
		0.f, 1.f, 0.f, offset.y,
		0.f, 0.f, 1.f, offset.z,
		0.f, 0.f, 0.f, 1.f
	};
}
				  
/*static*/ Matrix Matrix::CreateProjection(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
{
	assert(nearPlaneDistance > 0.f && "Near plane distance cannot be less than, or equal to zero");
	assert(farPlaneDistance > 0.f && "Far plane distance cannot be less than, or equal to zero");
	assert(farPlaneDistance > nearPlaneDistance && "Near plane distance is greater than far plane distance!");

	float num1 = float(1.f / tan(double(fieldOfView) * 0.5));
	float num2 = num1 / aspectRatio;

	Matrix matrix;
	matrix.m11 = num2;
	matrix.m12 = matrix.m13 = matrix.m14 = 0.0f;
	matrix.m22 = num1;
	matrix.m21 = matrix.m23 = matrix.m24 = 0.0f;
	matrix.m31 = matrix.m32 = 0.0f;
	matrix.m33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
	matrix.m34 = -1.f;
	matrix.m41 = matrix.m42 = matrix.m44 = 0.0f;
	matrix.m43 = (float)((double)nearPlaneDistance * (double)farPlaneDistance / ((double)nearPlaneDistance - (double)farPlaneDistance));
	return matrix;
}
/*static*/ Matrix Matrix::CreateLookAt(Vector3& cameraPosition, Vector3& cameraLookAt, Vector3& up)
{
	Vector3 vector3_1 = Vector3::Normalize(cameraPosition - cameraLookAt);
	Vector3 vector3_2 = Vector3::Normalize(Vector3::Cross(up, vector3_1));
	Vector3 vector1 = Vector3::Cross(vector3_1, vector3_2);

	Matrix matrix;
	matrix.m11 = vector3_2.x;
	matrix.m12 = vector1.x;
	matrix.m13 = vector3_1.x;
	matrix.m14 = 0.0f;
	matrix.m21 = vector3_2.y;
	matrix.m22 = vector1.y;
	matrix.m23 = vector3_1.y;
	matrix.m24 = 0.0f;
	matrix.m31 = vector3_2.z;
	matrix.m32 = vector1.z;
	matrix.m33 = vector3_1.z;
	matrix.m34 = 0.0f;
	matrix.m41 = -Vector3::Dot(vector3_2, cameraPosition);
	matrix.m42 = -Vector3::Dot(vector1, cameraPosition);
	matrix.m43 = -Vector3::Dot(vector3_1, cameraPosition);
	matrix.m44 = 1.f;
	return matrix;
}
#pragma endregion

#pragma region operators
Matrix operator+ (Matrix& a, Matrix& b)
{
	Matrix matrix;
	for (int i = 0; i < 16; i++)
	{
		matrix.e[i] = a.e[i] + b.e[i];
	}

	return matrix;
}
Matrix operator- (Matrix& a , Matrix& b)
{
	Matrix matrix;
	for (int i = 0; i < 16; i++)
		matrix.e[i] = a.e[i] - b.e[i];

	return matrix;
}
Matrix operator* (Matrix& a , Matrix& b)
{
	Vector4 rows_a[4] =
	{
		{ a.m11, a.m12, a.m13, a.m14 },
		{ a.m21, a.m22, a.m23, a.m24 },
		{ a.m31, a.m32, a.m33, a.m34 },
		{ a.m41, a.m42, a.m43, a.m44 }
	};	
	Vector4 colomns_b[4] =
	{
		{ b.m11, b.m21, b.m31, b.m41 },
		{ b.m12, b.m22, b.m32, b.m42 },
		{ b.m13, b.m23, b.m33, b.m43 },
		{ b.m14, b.m24, b.m34, b.m44 },
	};

	Matrix m;

	for (int x = 0; x < 4; x++)
	{
		float current = 0.f;
		for (int y = 0; y < 4; y++)
		{
			current = rows_a[x] * colomns_b[y];
			m.e[y + x * 4] = current;
		}
	}

	return m;
}
Vector3 operator* (Matrix& m, Vector3& v)
{
	Vector4 outval;
	Vector4 vv = Vector4(v, 0.f);

	Vector4 rows[4] =
	{
		{ m.m11, m.m12, m.m13, m.m14 },
		{ m.m21, m.m22, m.m23, m.m24 },
		{ m.m31, m.m32, m.m33, m.m34 },
		{ m.m41, m.m42, m.m43, m.m44 }
	};

	outval.x = vv * rows[0];
	outval.y = vv * rows[1];
	outval.z = vv * rows[2];

	return Vector3(outval.x, outval.y, outval.z);
}
Matrix operator* (Matrix& m, float f)
{
	Matrix outval;

	for (int i = 0; i < 16; i++)
		m.e[i] *= f;

	return outval;
}

Matrix::operator D3DMATRIX(void)
{
	return *reinterpret_cast<D3DMATRIX*>(e);
}
Matrix::operator const D3DMATRIX*(void)
{
	return reinterpret_cast<D3DMATRIX*>(e);
}
#pragma endregion